﻿using JiveMessenger.User;
using JiveMessenger.Utilities;
using NLog;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;

namespace JiveMessenger
{
    public class ChatLog
    {
        private readonly object LockObject = new object();
        private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
        //private bool _incrementSessionID;
        private string _loggedInUserName;
        private string _logName;
        private string _logFolder;
        private int _sessionID;
        public List<LogElement> ExistingLog { get; set; }
        public List<LogElement> Log { get; set; }

        public event EventHandler LogLoaded;
        public ChatLog(string logName, string logFolder, string loggedInUserName)
        {
            _logName = logName;
            Log = new List<LogElement>();
            ExistingLog = new List<LogElement>();
            this._logFolder = logFolder;
            this._loggedInUserName = loggedInUserName;

            Task.Factory.StartNew(() =>
                {
                    lock (LockObject)
                    {
                        this.ExistingLog = LoadLog(out _sessionID);
                        IncrementSessionID();
                        if (LogLoaded != null)
                            LogLoaded(null, null);
                    }
                });



        }

        public ChatLog()
        {
            Log = new List<LogElement>();
        }

        public void IncrementSessionID()
        {
            //_incrementSessionID = true;
            Interlocked.Increment(ref _sessionID);
        }
        public void AddLogElement(LogElement element)
        {

            this.Log.Add(element);

        }
        public void Clear()
        {
            this.Log.Clear();
        }


        private List<LogElement> LoadLog(out int sessionID)
        {
            sessionID = 0;
            List<LogElement> logs = new List<LogElement>();
            int lastSession = -1;
            string chatTarget = "";
            XmlReaderSettings settings = new XmlReaderSettings { IgnoreComments = true, IgnoreWhitespace = true };

            chatTarget = _logName;

            FileInfo fi = new FileInfo(GenerateLogName(this._loggedInUserName, chatTarget, ProgramSettings.Instance.LogFolder));
            if (!fi.Exists)
            {
                fi.Create().Close();
            }
            else
            {
                XmlReader r = XmlReader.Create(fi.FullName, settings);
                try
                {
                    r.MoveToContent();
                    r.Read();

                    while (!r.EOF)
                    {
                        if (r.Name == "Log" && !r.IsStartElement())
                            break; // end of it all
                        if (r.Name == "Message" && r.IsStartElement())
                        {
                            LogElement l = new LogElement(r.ReadSubtree());
                            if (l.IsOK())
                            {
                                logs.Add(l);
                                if (lastSession < l.SessionID)
                                    lastSession = l.SessionID;
                            }
                        }
                        r.Read();
                    }
                }
                catch (Exception err)
                {
                    Logger.ErrorException("Error loading log", err);
                    logs = new List<LogElement>();
                }
                finally
                {
                    r.Close();
                    sessionID = lastSession;

                }
            }
            return logs;
        }

        public void WriteLog()
        {
            if (String.IsNullOrEmpty(_logName) || this.Log.Count == 0 || ExistingLog == null)
                return;

            lock (LockObject)
            {
                string logFolder = _logFolder;
                //     int sessionID = 0;
                //   List<LogElement> existingLogs = this.LoadLog(out sessionID);
                //if (_incrementSessionID)
                //{
                //    sessionID++;
                //    _incrementSessionID = false;
                //}
                XmlWriterSettings settings = new XmlWriterSettings { Indent = true, NewLineOnAttributes = true };
                bool createNewFile = false;
                FileInfo fi = new FileInfo(GenerateLogName(this._loggedInUserName, _logName, logFolder));
                if (fi.Directory != null && !fi.Directory.Exists)
                {
                    fi.Directory.Create();
                }
                if (!File.Exists(fi.FullName))
                    createNewFile = true;
                if (!File.Exists(StaticMethods.CombinePath(logFolder, "MessageLog.xsl")))
                    File.WriteAllText(StaticMethods.CombinePath(logFolder, "MessageLog.xsl"), Properties.Resources.MessageLog);
           //     bool success = true;
                List<LogElement> writtenLogElements = new List<LogElement>();
                try
                {
                    using (FileStream fs = !File.Exists(fi.FullName + "temp") ? File.Create(fi.FullName + "temp") : StaticMethods.WaitForFile(fi.FullName + "temp", FileMode.Open, FileAccess.ReadWrite, 1000))
                    {
                        if (fs == null)
                            return;

                        using (XmlWriter w = XmlWriter.Create(fs, settings))
                        {

                            const string strPi = "type='text/xsl' href='../MessageLog.xsl'";
                            w.WriteProcessingInstruction("xml-stylesheet", strPi);



                            w.WriteStartElement("Log");
                            ExistingLog.Sort();
                            this.Log.Sort();
                            foreach (LogElement oldL in ExistingLog)
                                oldL.WriteXml(w);
                            foreach (LogElement l in this.Log)
                            {
                                if (ExistingLog.Any(x => Math.Abs(x.TimeStamp.Subtract(l.TimeStamp).TotalMilliseconds) <= 1))
                                    continue;
                                l.SessionID = _sessionID;
                                l.WriteXml(w);
                                writtenLogElements.Add(l);
                            }
                            w.WriteEndElement();//Log
                        }
                    }
                    if (fi.Exists)
                        fi.Delete();

                    File.Move(fi.FullName + "temp", fi.FullName);
                    //When we switch to a new file, we need to clear the log lists to avoid recreating the whole file on next save
                    if (createNewFile)
                    {
                        this.ExistingLog.Clear();
                        this.Log.Clear();
                    }
                    else//If we keep writing to the same file, move the current log elements to the existinglogs list so they will be written on next save.
                    {
                        foreach (LogElement l in writtenLogElements)
                        {
                            this.Log.Remove(l);
                            this.ExistingLog.Add(l);
                        }
                    }
                }
                catch (Exception ex)
                {
                    Logger.ErrorException("Error saving log", ex);
                 //   success = false;
                }
                finally
                {
                    //w.Close();
                    //fs.Close();
                    //if (success)
                    //{
                    //    if (fi.Exists)
                    //        fi.Delete();

                    //    File.Move(fi.FullName + "temp", fi.FullName);

                    //    //When we switch to a new file, we need to clear the log lists to avoid recreating the whole file on next save
                    //    if (createNewFile)
                    //    {
                    //        this.ExistingLog.Clear();
                    //        this.Log.Clear();
                    //    }
                    //    else//If we keep writing to the same file, move the current log elements to the existinglogs list so they will be written on next save.
                    //    {
                    //        foreach (LogElement l in writtenLogElements)
                    //        {
                    //            this.Log.Remove(l);
                    //            this.ExistingLog.Add(l);
                    //        }
                    //    }
                    //}
                }
            }
        }

        private string GenerateLogName(string loggedInUserName, string logName, string folderPath)
        {
            string validatedName = logName;
            int tries = 1;

            DirectoryInfo saveDir = new DirectoryInfo(StaticMethods.CombinePath(folderPath, loggedInUserName));
            if (!saveDir.Exists)
                saveDir.Create();

            while (File.Exists(StaticMethods.CombinePath(saveDir, Path.ChangeExtension(validatedName, ".xml"))))
            {
                FileInfo fi = new FileInfo(StaticMethods.CombinePath(saveDir, Path.ChangeExtension(validatedName, ".xml")));
                if (fi.Length < 3000 * 1024)
                {
                    if (!File.Exists(StaticMethods.CombinePath(saveDir, string.Format("{0} ({1}){2}", Path.GetFileNameWithoutExtension(logName), tries + 1, Path.GetExtension(logName)))))
                    {
                        return fi.FullName;
                    }
                }

                validatedName = string.Format("{0} ({1}){2}", Path.GetFileNameWithoutExtension(logName), tries++, Path.GetExtension(logName));
            }
            return StaticMethods.CombinePath(saveDir, Path.ChangeExtension(validatedName, ".xml"));
        }
    }
}
